/* uedb.c */
#include <windows.h>
#include <stdlib.h>

/* UELogger Support */
#include "litelog.h"
extern int logfd;

/* API declaration */
#include "uedb.h"
#include "uedb_param.h"
#include "uedb_sqlite3.h"

int uedb_connect(const char *dbconnection, uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */
	const char *type;                                    /* database type */
	uedb_param *paramlist = 0;                           /* parameter list in DB connection string */
	void *pconnection;                                   /* database connection */

	dbglogex("Enter uedb_connect(%s, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, dbconnection, uedb);

	/* parameter check */
	if (uedb)
	{
		memset(uedb, 0, sizeof(uedb_connection));

		/* parse connection string */
		if (dbconnection)
			ret = parseparams(&paramlist, dbconnection);

		/* check if we parse connection string success */
		if (ret == UE_DB_ERR_SUCCESS)
		{
			/* check database type */
			type = getparam(paramlist, "type");
			/* by default, we'll use sqlite3 */
			if (type == 0)
			{
				uedb->type = UE_DB_TYPE_SQLITE3;

				pconnection = malloc(sizeof(sqlite3_connection));
				if (pconnection)
				{
					ret = uedb_sqlite3_connect(getparam(paramlist, "path"), getparam(paramlist, "dbname"), pconnection);
					if (ret == UE_DB_ERR_SUCCESS)
						uedb->pconnection = pconnection;
					else
						free(pconnection);
				} /* malloc memory for sqlite3 connection success */
				else
				{
					dbglog("Malloc memory for sqlite3 connection failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
					ret = UE_DB_ERR_OUT_OF_MEMORY;
				} /* malloc memory for sqlite3 connection failed */
			} /* do not specified type, use default type */
			/* if database type is sqlite3 */
			else if (strcmp(type, "SQLITE3") == 0)
			{
				uedb->type = UE_DB_TYPE_SQLITE3;

				pconnection = malloc(sizeof(sqlite3_connection));
				if (pconnection)
				{
					ret = uedb_sqlite3_connect(getparam(paramlist, "path"), getparam(paramlist, "dbname"), pconnection);
					if (ret == UE_DB_ERR_SUCCESS)
						uedb->pconnection = pconnection;
					else
						free(pconnection);
				} /* malloc memory for sqlite3 connection success */
				else
				{
					dbglog("Malloc memory for sqlite3 connection failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
					ret = UE_DB_ERR_OUT_OF_MEMORY;
				} /* malloc memory for sqlite3 connection failed */
			} /* Type: SQLite3 */
			else
			{
				ret = UE_DB_ERR_INVALID_PARAMETER;
			}
		} /* parse connection parameter success */
		else
		{
			dbglog("Parse connection parameter failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		} /* parse connection parameter failed */
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_connect()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
	return ret;
}

int uedb_create(const char *dbspec, uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */
	const char *dbname;                                  /* database name */
	void *pconnection;                                   /* database connection */

	dbglogex("Enter uedb_create(%s, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, dbspec, uedb);

	/* parameter check */
	if (uedb)
	{
		/* check connection type */
		switch (uedb->type)
		{
		/* SQLite3 */
		case UE_DB_TYPE_SQLITE3:
			dbglog("Database type is sqlite3", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			if (uedb->pconnection)
			{
				dbglog("Database connection is already established.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
				ret = uedb_sqlite3_create(dbspec, uedb->pconnection);
			} /* database connection already established */
			else
			{
				dbglog("No existing database connection.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
				pconnection = (sqlite3_connection*)malloc(sizeof(sqlite3_connection));
				if (pconnection)
				{
					dbglog("Malloc memory for new connection success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

					/* parse dbname from dbspec */
					dbname = dbspec;

					ret = uedb_sqlite3_connect(0, dbname, pconnection);
					if (ret == UE_DB_ERR_SUCCESS)
					{
						dbglog("Connect to database success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

						uedb->pconnection = pconnection;
						ret = uedb_sqlite3_create(0, uedb->pconnection);
					} /* connect to database success */
					else
					{
						dbglogex("Connect to database failed with return value: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, ret);
						free(uedb);
					} /* connect to database failed */
				} /* malloc memory for new connection success */
				else
				{
					dbglog("Malloc memory for sqlite3 connection failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
					ret = UE_DB_ERR_OUT_OF_MEMORY;
				} /* malloc memory for new connection failed */
			} /* no existing database connection */
			break; /* finished handle sqlite3 case */
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		} /* finish check connection type */
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_create", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_close(uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_close(%x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, uedb);

	if (uedb)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_close(uedb->pconnection);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}

		if (ret == UE_DB_ERR_SUCCESS)
			uedb->pconnection = 0;
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_close", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_execute(const char *sql, uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_execute(%s, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, sql, uedb);

	if (uedb)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_execute(sql, uedb->pconnection);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_execute", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_execute_ex(const char *sql, const uedb_parameter *params, const unsigned int param_num, uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_execute_ex(%s, %x, %d, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, sql, params, param_num, uedb);

	if (uedb && sql && strlen(sql) > 0 && (param_num == 0 || param_num > 0 && params))
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_execute_ex(sql, params, param_num, uedb->pconnection);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null or parameters invalid", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_execute_ex", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_query(const char *sql, uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_query(%s, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, sql, uedb);

	if (uedb)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_query(sql, uedb->pconnection);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_query", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_close_query(uedb_connection *uedb)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_close_query(%x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, uedb);

	if (uedb)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_close_query(uedb->pconnection);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_close_query", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_rows(uedb_connection *uedb, int *prows)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_rows(%x, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, uedb, prows);

	if (uedb && prows)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_rows(uedb->pconnection, prows);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb or prows is null", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_rows", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_result_by_name(const char *column_name, const int row, uedb_connection *uedb, const char **presult)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_result(%s, %d, %x, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, column_name, row, uedb, presult);

	if (column_name && strlen(column_name) > 0 && row >= 0 && uedb && presult)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_result_by_name(column_name, row, uedb->pconnection, presult);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb or presult is null or column name empty or row index invalid", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_result", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}

int uedb_result_by_index(const int column, const int row, uedb_connection *uedb, const char **presult)
{
	int ret = UE_DB_ERR_SUCCESS;                         /* return code */

	dbglogex("Enter uedb_result(%d, %d, %x, %x)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, column, row, uedb, presult);

	if (column >= 0 && row >= 0 && uedb && presult)
	{
		switch (uedb->type)
		{
		case UE_DB_TYPE_SQLITE3:
			ret = uedb_sqlite3_result_by_index(column, row, uedb->pconnection, presult);
			break;
		default:
			ret = UE_DB_ERR_INVALID_PARAMETER;
			break;
		}
	} /* parameter check success */
	else
	{
		dbglog("Invalid parameter, uedb or presult is null or column index is invalid or row index is invalid", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		ret = UE_DB_ERR_INVALID_PARAMETER;
	} /* parameter check failed */

	dbglog("Exit uedb_result", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return ret;
}